import * as THREE from '../../js/threejs/build/three.module.js'

import {Config} from '../../js/threejs/Config.js'
import {Loader} from '../../js/threejs/Loader.js'
import {LoaderRemote} from '../../js/threejs/Loader.Remote.js'
import {Strings} from '../../js/threejs/Strings.js'
import {Storage as StorageCache} from '../../js/threejs/Storage.js'
import {StorageRemote} from '../../js/threejs/Storage.Remote.js'

import {Request} from '../../js/threejs/Request.js'
import MqttClient from './MqttClient.js'
import {SunTools} from "../../js/sun.tools.js";

var Displayer = function () {
  var scope = this

  this.VIEWPORT_WIDTH = 1050
  this.VIEWPORT_HEIGHT = 650

  console.log(this.VIEWPORT_WIDTH, this.VIEWPORT_HEIGHT)

  this.DEFAULT_CAMERA = new THREE.PerspectiveCamera(45, this.VIEWPORT_WIDTH / this.VIEWPORT_HEIGHT, 0.1, 5000)
  this.DEFAULT_CAMERA.name = 'Camera'
  this.DEFAULT_CAMERA.position.set(0, 5, 10)
  this.DEFAULT_CAMERA.lookAt(new THREE.Vector3())



  /*
  * http://zhouwr.iot:8080/jeecg-boot/display/index.html?mode=display&baseUrl=/jeecg-boot&token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NDAzMTc1MDUsInVzZXJuYW1lIjoiYWRtaW4ifQ.MKAzu_p7bTRJbWXpjTCOq48RoXYECsegrgmBWhrJ_ok
  * */
  this.USERNAME = this.getQueryVariable('username') // 权限认证token
  this.TOKEN = this.getQueryVariable('token') // 权限认证token
  this.SCENE_ID = this.getQueryVariable('sceneId')
  this.BASE_URL = this.getQueryVariable('baseUrl') // 后台项目跟路径
  console.log('USERNAME', this.USERNAME)
  console.log('SCENE_ID', this.SCENE_ID)
  console.log('BASE_URL', this.BASE_URL)
  console.log('TOKEN', this.TOKEN)

  this.deployConfig = {}
  this.configObjTimer = null
  this.labelObjects = []
  this.request = new Request(this.BASE_URL, this.TOKEN)
  this.sunTools = new SunTools()

  this.deviceConfigCount = 0

  const Signal = signals.Signal

  this.signals = {
    transformModeChanged: new Signal(),
    snapChanged: new Signal(),
    spaceChanged: new Signal(),
    rendererChanged: new Signal(),
    rendererUpdated: new Signal(),
    sceneRendered: new Signal(),
    sceneFogChanged: new Signal(),
    sceneGraphChanged: new Signal(),
    geometryChanged: new Signal(),
    cameraAdded: new Signal(),
    cameraRemoved: new Signal(),
    cameraChanged: new Signal(),
    objectSelected: new Signal(),
    objectFocused: new Signal(),
    objectAdded: new Signal(),
    objectChanged: new Signal(),
    objectRemoved: new Signal(),
    objectRemoteSaved: new Signal(),
    helperAdded: new Signal(),
    helperRemoved: new Signal(),
    refreshSidebarObject3D: new Signal(),

    materialAdded: new Signal(),
    materialChanged: new Signal(),
    materialRemoved: new Signal(),

    windowResize: new Signal(),

    showHelpersChanged: new Signal(),
    enableTransformControlsChanged: new Signal(), // zhouwr add by 2020-11-04
    objectConfiged: new Signal(), //
    objectAllConfiged: new Signal(), //
    setReaderSized: new Signal(),
    deployConfigLoaded: new Signal(),
    searchStarted: new Signal(),
    searchEnded: new Signal(),

    // 设备详情
    deviceDetailed: new Signal(),

    // 接收到数据
    dataReceived: new Signal(),

    deviceOnline: new Signal(),
    deviceOffline: new Signal(),
    deviceFunction: new Signal(),

    deviceMenued: new Signal(),
    deviceLine: new Signal(),
    deviceDeline: new Signal(),

    onclick: new Signal(),
    onrightclick: new Signal(),
    ondbclick: new Signal(),

    removeLabel: new Signal(),
    deployReload: new Signal(),
    showShadow: new Signal(),
  }

  // this.axios = axios.create;
  this.config = new Config()
  this.storage = new StorageCache()
  this.strings = new Strings(this.config)

  this.loader = new Loader(this)
  this.loaderRemote = new LoaderRemote(this)

  this.renderer = new THREE.WebGLRenderer({antialias: true})
  this.camera = this.DEFAULT_CAMERA.clone()

  this.scene = new THREE.Scene()
  this.scene.name = 'Scene'
  // this.scene.background = new THREE.Color(0xaaaaaa)

  this.sceneHelpers = new THREE.Scene()
  this.sceneHelpers.add(new THREE.BoxHelper()) //不要将helper直接添加到scene中, 有可能导致选中物体时意外选中helper;

  this.mixer = new THREE.AnimationMixer(this.scene)
  this.selected = null
  this.rightClicked = null
  this.helpers = {}
  this.scripts = {}

  this.storageRemote = new StorageRemote(this)

  this.addCamera(this.camera)

  this.signals.deployConfigLoaded.add((deployConfig) => {
    scope.deployConfig = deployConfig
    // 创建一个现场场景分组
    if(!window.sceneGroup) {
      const sceneGroup = window.sceneGroup = new THREE.Group();
      sceneGroup.userData.type = 'sceneGroup'
      sceneGroup['name'] = 'sceneGroup'
      displayer.addObject(sceneGroup)
    }
    sceneGroup['uuid'] = scope.deployConfig.id
    sceneGroup['name'] = scope.deployConfig.name

    if(!scope.mqttClient) {
      scope.mqttClient = new MqttClient(scope, deployConfig.id)
    }
    console.log('MqttClient', scope.mqttClient)

    window.setInterval(() => {
      scope.request.getAction('/display/keepLogin', {})
      console.log('<<<<<<<<<<< 保存在线 >>>>>>>>>>>')
    }, 1000 * 60 * 5)
  })

  // zhouwr
  let timer
  this.signals.objectAllConfiged.add(function () {
    clearTimeout(timer)
    timer = setTimeout(() => {
      console.log('>>>>>>>>>> 整体场景居中显示 <<<<<<<<<<<')
      // displayer.signals.objectSelected.dispatch(sceneGroup)
      displayer.signals.objectFocused.dispatch(sceneGroup)
    }, 200)
  })
}

Displayer.prototype = {
  /**
   * 配置模型对象
   * @author zhouwr
   * @param object 模型对象
   * @param deployConfig 部署配置信息
   * @returns {*}
   */
  configObj: function (object, deployConfig) {
    let scope = this
    this.signals.objectAllConfiged.active = false

    console.log('配置模型对象：', deployConfig.id, deployConfig.name)
    object = object.scene || object
    object.uuid = deployConfig.id
    object.name = deployConfig.type === 'scene'?deployConfig.modelFile.substring(0, deployConfig.modelFile.lastIndexOf('_')):deployConfig.name
    object.file = deployConfig.modelFile
    object.userData['modelFile'] = deployConfig.modelFile
    object.userData['type'] = deployConfig.type
    object.userData['scheme'] = deployConfig.schemeId
    if(deployConfig.type === 'scene') {
      object.userData['modelId'] = deployConfig.modelId
    }

    if (deployConfig.attribute) {
      let position = deployConfig.attribute['position']
      let rotation = deployConfig.attribute['rotation']
      let scale = deployConfig.attribute['scale']

      if (position) object.position.set(position.x || 0, position.y || 0, position.z || 0)
      if (rotation) object.rotation.set(rotation.x || 0, rotation.y || 0, rotation.z || 0)
      if (scale) object.scale.set(scale.x || 1, scale.y || 1, scale.z || 1)
    }
    this.signals.objectConfiged.dispatch(object)

    this.signals.objectAllConfiged.active = false
    console.log('%c 已配置设备数：%d/%d', 'color:blue;', ++scope.deviceConfigCount, scope.deployConfig.deviceInstances.length + scope.deployConfig.modelFile.split(',').length)
    // 已配置设备的数量>=所有设备数+场景数
    if(scope.deviceConfigCount >= scope.deployConfig.deviceInstances.length + scope.deployConfig.modelFile.split(',').length ) {
      scope.signals.objectAllConfiged.active = true
      scope.signals.objectAllConfiged.dispatch()
      console.log('%c objectAllConfiged.dispatch() >>>>>> ', 'color:#0f0;')
      scope.deviceConfigCount = 0
    } else{
      this.signals.objectAllConfiged.active = false
    }
    return object
  },

  /**
   * 坐标3维转2维
   * @returns {{x: number, y: number}}
   * @param vector3
   */
  transPosition: function (vector3) {
    let world_vector = new THREE.Vector3(vector3.x, vector3.y, vector3.z);
    let vector = world_vector.project(this.camera);
    let halfWidth = this.VIEWPORT_WIDTH / 2
    let halfHeight = this.VIEWPORT_HEIGHT / 2;
    return {
      x: Math.round(vector.x * halfWidth + halfWidth), y: Math.round(-vector.y * halfHeight + halfHeight)
    }
  },

  /**
   * 计算对象的边缘球直径
   */
  objectBoundingRadius: function(target) {
    let center = target.position
    if(target.userData.type === 'sceneGroup'){
      center = new THREE.Vector3().set(0,0,0)
    }
    let box = new THREE.Box3()
    box.setFromObject(target)
    box.getCenter(center)
    return box.getBoundingSphere(target).radius
  },

  /**
   * 判断两个矩形是否相交
   * @param domA
   * @param domB
   * @returns {boolean}
   */
  checkIntersect: function (domA, domB) {
    const objOne = $(domA)
    const objTwo = $(domB)
    const offsetOne = objOne.offset();
    const offsetTwo = objTwo.offset();
    const x1 = offsetOne.left;
    const y1 = offsetOne.top;
    const x2 = x1 + objOne.width();
    const y2 = y1 + objOne.height();

    const x3 = offsetTwo.left;
    const y3 = offsetTwo.top;
    const x4 = x3 + objTwo.width();
    const y4 = y3 + objTwo.height();

    const zx = Math.abs(x1 + x2 - x3 - x4);
    const x = Math.abs(x1 - x2) + Math.abs(x3 - x4);
    const zy = Math.abs(y1 + y2 - y3 - y4);
    const y = Math.abs(y1 - y2) + Math.abs(y3 - y4);
    return (zx < x && zy < y);
  },

  /**
   * @author zhouwr
   * @description 从路径中获取参数
   * @param  name 参数变量名
   */
  getQueryVariable: function (name) {
    let query = window.location.search.substring(1)
    let lets = query.split('&')
    for (let i = 0; i < lets.length; i++) {
      let pair = lets[i].split('=')
      if (pair[0] === name) {
        return pair[1]
      }
    }
    return ''
  },

  /**
   * 缓存加载时，配置整个场景
   * @param scene
   */
  setScene: function (scene) {
    const scope = this
    console.log('缓存加载时，配置整个场景 start')

    this.scene.uuid = scene.uuid
    this.scene.name = scene.name

    this.scene.background = scene.background !== null ? scene.background.clone() : null

    if (scene.fog !== null) this.scene.fog = scene.fog.clone()

    this.scene.userData = JSON.parse(JSON.stringify(scene.userData))

    // avoid render per object

    this.signals.sceneGraphChanged.active = false

    scene.children
      .filter(obj => obj.userData && obj.userData.modelFile)
      .map(obj => {
        /**
         * zhouwr add by 2020-11-06
         * 给模型对象添加属性file，用于模型加载时与配置信息比对文件
         */
        console.log(obj.userData.modelFile)
        obj['file'] = obj.userData.modelFile
        scope.addObject(obj)
      });
    this.signals.sceneGraphChanged.active = true
    this.signals.sceneGraphChanged.dispatch()

  },

  addObject: function (object, parent, index) {
    let scope = this

    if (!object.name) return 0

    if(object.userData.type === 'scene') {
      parent = sceneGroup
      object.uuid = sceneGroup.uuid + '-' + sceneGroup.children.length
      index = 0
    }
    console.log('%c addObject：', 'color:red;', object)
    if (parent === undefined) {
      scope.scene.add(object)
    } else {
      parent.children.splice(index, 0, object)
      object.parent = parent
    }

    this.signals.objectAdded.dispatch(object)
    this.signals.sceneGraphChanged.dispatch()
  },

  removeObject: function (object) {
    if (object.parent === null) return // avoid deleting the camera or scene

    var scope = this

    object.traverse(function (child) {
      scope.removeHelper(child)

      if (child.material !== undefined) scope.removeMaterial(child.material)
    })

    object.parent.remove(object)

    this.config.setKey('autosave', true)
    this.signals.objectRemoved.dispatch(object)
    this.signals.sceneGraphChanged.dispatch()
  },

  addAnimation: function (object, animations) {
    if (animations.length > 0) {
      this.animations[object.uuid] = animations
    }
  },

  addCamera: function (camera) {
    if (camera.isCamera) {
      this.signals.cameraAdded.dispatch(camera)
    }
  },

  removeHelper: function (object) {
    if (this.helpers[object.id] !== undefined) {
      var helper = this.helpers[object.id]
      helper.parent.remove(helper)
      delete this.helpers[object.id]
      this.signals.helperRemoved.dispatch(helper)
    }
  },

  setViewportCamera: function (uuid) {
    this.viewportCamera = this.cameras[uuid]
    this.signals.viewportCameraChanged.dispatch(this.viewportCamera)
  },

  /**
   * 获取整个对象
   * @author zhouwr
   * @date 2020-11-07
   * @param object 对象片段
   * @returns whole object 整体对象
   */
  getWholeObject: function (object) {
    if (object === null) return null
    let pobj = object.parent
    if (pobj.type === 'Scene') {
      return object
    } else {
      return this.getWholeObject(pobj)
    }
  },

  select: function (object) {
    let uuid = null
    if (object !== null) {
      uuid = object.uuid
    }
    console.log('选择对象', object)
    this.selected = object
    this.config.setKey('selected', uuid)
    this.signals.objectSelected.dispatch(object)
  },

  selectById: function (id) {
    if (id === this.camera.id) {
      this.select(this.camera)
      return
    }
    this.select(this.scene.getObjectById(id, true))
  },

  selectByUuid: function (uuid) {
    let scope = this
    this.scene.traverse(function (child) {
      if (child.uuid === uuid) {
        scope.select(child)
      }
    })
  },

  deselect: function () {
    this.select(null)
  },

  focus: function (object) {
    if (object !== undefined) {
      this.signals.objectFocused.dispatch(object)
    }
  },

  focusById: function (id) {
    this.focus(this.scene.getObjectById(id, true))
  },

  focusByUuid: function (uuid) {
    let scope = this
    this.scene.traverse(function (child) {
      if (child.uuid === uuid) {
        scope.focus(child)
      }
    })
  },

  clear: function () {
    this.storage.clear()

    this.camera.copy(this.DEFAULT_CAMERA)
    this.scene.name = 'Scene'
    this.scene.userData = {}
    this.scene.background = new THREE.Color(0xaaaaaa)
    this.scene.fog = null

    let objects = this.scene.children

    while (objects.length > 0) {
      this.removeObject(objects[0])
    }

    this.geometries = {}
    this.materials = {}
    this.textures = {}

    this.materialsRefCounter.clear()

    this.animations = {}
    this.mixer.stopAllAction()

    this.deselect()
  },

  fromJSON: function (json) {
    let scope = this
    console.log('缓存加载 start')
    const start = performance.now();

    let loader = new THREE.ObjectLoader()
    let camera = loader.parse(json.camera)
    // console.log(this.camera, camera)
    //
    this.camera.copy(camera)
    this.camera.aspect = this.DEFAULT_CAMERA.aspect
    this.camera.updateProjectionMatrix()

    this.scripts = json.scripts
    return new Promise((resolve, reject) => {
      loader.parse(json.scene, function (scene) {
        scope.setScene(scene)
        scope.signals.sceneGraphChanged.dispatch()
        console.log('[' + /\d\d\:\d\d\:\d\d/.exec(new Date())[0] + ']', '缓存加载整个场景 ' + (performance.now() - start).toFixed(2) + 'ms');
        return resolve(scene)
      })
    })
  },

  toJSON: function () {
    let scene = this.scene

    let objects = scene.children
    for(let i in objects) {
      if(objects[i].userData && objects[i].userData.type === 'sceneGroup'){
        for(let j in objects[i].children) {
          objects[i].children[j].uuid = objects[i].uuid +'-'+j
          objects.push(objects[i].children[j])
        }
        objects.splice(i, 1)
      }
    }
    // scene.children = objects
    return {
      metadata: {}, project: {
        shadows: this.config.getKey('project/renderer/shadows'),
      },
      camera: this.camera.toJSON(),
      scripts: this.scripts,
      scene: scene.toJSON()
    }
  },

  objectByUuid: function (uuid) {
    return this.scene.getObjectByProperty('uuid', uuid, true)
  },

  execute: function (cmd, optionalName) {
    this.history.execute(cmd, optionalName)
  },
}

export default Displayer